#ifndef PORTABLE_HANDLER_TABLE_H_
#define PORTABLE_HANDLER_TABLE_H_

#include "src/pbc/portable-bit-field.h"

#include <vector>

namespace portable {

// HandlerTable is a byte array containing entries for exception handlers in
// the code object it is associated with. Layout looks as follows:
//      [ range-start , range-end , handler-offset , handler-data ]
class HandlerTable {
 public:
  // Conservative prediction whether a given handler will locally catch an
  // exception or cause a re-throw to outside the code boundary. Since this is
  // undecidable it is merely an approximation (e.g. useful for debugger).
  enum CatchPrediction {
    UNCAUGHT,     // The handler will (likely) rethrow the exception.
    CAUGHT,       // The exception will be caught by the handler.
    PROMISE,      // The exception will be caught and cause a promise rejection.
    ASYNC_AWAIT,  // The exception will be caught and cause a promise rejection
                  // in the desugaring of an async function, so special
                  // async/await handling in the debugger can take place.
    UNCAUGHT_ASYNC_AWAIT,  // The exception will be caught and cause a promise
                           // rejection in the desugaring of an async REPL
                           // script. The corresponding message object needs to
                           // be kept alive on the Isolate though.
  };

  explicit HandlerTable(int size);
  ~HandlerTable() = default;

  // Getters for handler table based on ranges.
  int GetRangeStart(int index) const;
  int GetRangeEnd(int index) const;
  int GetRangeHandler(int index) const;
  int GetRangeData(int index) const;

  // Setters for handler table based on ranges.
  void SetRangeStart(int index, int value);
  void SetRangeEnd(int index, int value);
  void SetRangeHandler(int index, int offset, CatchPrediction pred);
  void SetRangeData(int index, int value);

  // Returns the required length of the underlying byte array.
  static int LengthForRange(int entries);

  // Returns the number of entries in the table.
  int NumberOfRangeEntries() const;
  int NumberOfReturnEntries() const;

  void HandlerTableRangePrint(std::ostream& os);

  bool HandlerWasUsed(int index) const;
  void MarkHandlerUsed(int index);
  // Getters for handler table based on ranges.
  CatchPrediction GetRangePrediction(int index) const;

  int length();

  int GetRangeHandlerBitfield(int index) const;

  static const int kNoHandlerFound = -1;

 private:
  struct RangeEntry {
    int range_start;      // Bytecode offset starting try-region.
    int range_end;        // Bytecode offset ending try-region.
    int range_handler;    // Bytecode offset of handler target & Prediction.
    int range_data;       // Register number holding context for handler.
  };

  std::vector<RangeEntry> raw_entries_data_;


  // Encoding of the {handler} field.
  using HandlerPredictionField = BitField<CatchPrediction, 0, 3>;
  using HandlerWasUsedField = HandlerPredictionField::Next<bool, 1>;
  using HandlerOffsetField = HandlerWasUsedField::Next<int, 28>;
};

}  // namespace portable

#endif  // PORTABLE_HANDLER_TABLE_H_

